package com.ideaaedi.springcloud.jd.user.service.impl;

import cn.xuyanwu.spring.file.storage.FileInfo;
import cn.xuyanwu.spring.file.storage.FileStorageService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Sets;
import com.ideaaedi.springcloud.jd.commonds.constant.JdSymbolConstant;
import com.ideaaedi.springcloud.jd.commonds.constant.PathConstant;
import com.ideaaedi.springcloud.jd.commonds.entity.BaseCodeMsgEnum;
import com.ideaaedi.springcloud.jd.commonds.entity.PageInfo;
import com.ideaaedi.springcloud.jd.commonds.entity.user.po.SysFilePO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.vo.req.SysFileListReqVO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.vo.resp.SysFileBatchUploadRespVO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.vo.resp.SysFileDetailRespVO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.vo.resp.SysFileDownloadImgRespVO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.vo.resp.SysFileListRespVO;
import com.ideaaedi.springcloud.jd.commonds.exception.BaseException;
import com.ideaaedi.springcloud.jd.commonspring.mapper.SysFileMapper;
import com.ideaaedi.springcloud.jd.commonspring.mybatisplus.ServiceImplExt;
import com.ideaaedi.springcloud.jd.commonspring.util.JdContextUtil;
import com.ideaaedi.springcloud.jd.commonspring.validator.SysValidator;
import com.ideaaedi.springcloud.jd.user.service.SysFileService;
import com.ideaaedi.springcloud.jd.user.service.SysTenantService;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 系统-文件 服务实现类
 * </p>
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 2021.0.5.B
 */
@Service
public class SysFileServiceImpl extends ServiceImplExt<SysFileMapper, SysFilePO> implements SysFileService {
    
    @Resource
    private FileStorageService fileStorageService;
    
    @Resource
    private SysTenantService sysTenantService;
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public SysFileDetailRespVO upload(MultipartFile file, @Nullable String tenant) {
        if (StringUtils.isNotBlank(tenant)) {
            SysValidator.verifyCouldUpdateThisTenant(tenant);
        }
        
        // 上传
        FileInfo fileInfo = doUpload(file);
        
        // 插入数据库
        return saveAndGetResp(fileInfo, tenant);
    }
    
    @Override
    public SysFileDetailRespVO upload(@NonNull String originalFilename, @NonNull byte[] fileBytes, @Nullable String tenant) {
        Assert.isTrue(StringUtils.isNotBlank(originalFilename), "originalFilename cannot be blank.");
        Objects.requireNonNull(fileBytes, "fileBytes cannot be null.");
        if (StringUtils.isNotBlank(tenant)) {
            SysValidator.verifyCouldUpdateThisTenant(tenant);
        }
        // 上传
        FileInfo fileInfo = fileStorageService.of(fileBytes)
                .setSaveFilename(originalFilename)
                .setOriginalFilename(originalFilename)
                .setPath(LocalDateTime.now().format(PathConstant.FILE_DATE_PATH_FORMATTER) + JdSymbolConstant.SLASH
                        + JdContextUtil.currUserIdNonNull() + JdSymbolConstant.SLASH)
                .upload();
        
        // 插入数据库
        return saveAndGetResp(fileInfo, tenant);
    }
    
    @NotNull
    private SysFileDetailRespVO saveAndGetResp(FileInfo fileInfo, @Nullable String tenant) {
        // save
        SysFilePO po = new SysFilePO();
        BeanUtils.copyProperties(fileInfo, po);
        po.setId(IdWorker.getId());
        po.setStoragePlatform(fileInfo.getPlatform());
        po.setTenant(tenant);
        save(po);
        
        // resp
        return detail(po.getId());
    }
    
    @Override
    public SysFileBatchUploadRespVO batchUpload(List<MultipartFile> fileList, @Nullable String tenant) {
        if (StringUtils.isNotBlank(tenant)) {
            SysValidator.verifyCouldUpdateThisTenant(tenant);
        } else {
            tenant = JdContextUtil.currUserTenantNonNull();
        }
        List<SysFilePO> poList = new ArrayList<>(fileList.size());
        for (MultipartFile file : fileList) {
            // 文件上传
            FileInfo fileInfo = doUpload(file);
            
            // 生成po模型
            SysFilePO po = new SysFilePO();
            BeanUtils.copyProperties(fileInfo, po);
            po.setId(IdWorker.getId());
            po.setTenant(tenant);
            po.setStoragePlatform(fileInfo.getPlatform());
            poList.add(po);
        }
        
        // 入库
        saveBatch(poList);
    
        // 查询租户信息
        Map<String, String> tenantAndNameMap = sysTenantService.mappingTenantName(Sets.newHashSet(tenant), true);
        
        // 组装出参
        List<SysFileDetailRespVO> fileDetailList = new ArrayList<>(poList.size());
        Map<String, Long> fileNameAndFileIdMap = new LinkedHashMap<>(16);
        for (SysFilePO po : poList) {
            fileNameAndFileIdMap.put(po.getOriginalFilename(), po.getId());
            SysFileDetailRespVO detail = new SysFileDetailRespVO();
            BeanUtils.copyProperties(po, detail);
            detail.setTenantName(tenantAndNameMap.get(detail.getTenant()));
            fileDetailList.add(detail);
        }
        SysFileBatchUploadRespVO resp = new SysFileBatchUploadRespVO();
        resp.setFileDetailList(fileDetailList);
        resp.setFileNameAndFileIdMap(fileNameAndFileIdMap);
        return resp;
    }
    
    @NonNull
    @Override
    public Pair<SysFilePO, byte[]> loadFile(Long fileId) {
        Objects.requireNonNull(fileId, "fileId cannot be null.");
        SysFilePO po = getById(fileId);
        if (po == null) {
            throw new BaseException(BaseCodeMsgEnum.FILE_NON_EXIST);
        }
        FileInfo fileInfo = new FileInfo();
        BeanUtils.copyProperties(po, fileInfo);
        fileInfo.setPlatform(po.getStoragePlatform());
        byte[] body = fileStorageService.download(fileInfo).bytes();
        if (body == null) {
            throw new BaseException(BaseCodeMsgEnum.LOAD_DFS_FAIL_FAIL);
        }
        return Pair.of(po, body);
    }
    
    
    @Override
    public SysFileDownloadImgRespVO downloadImg(Long fileId) {
        Pair<SysFilePO, byte[]> fileNameAndContentPair = loadFile(fileId);
        final SysFileDownloadImgRespVO downloadImgResp = new SysFileDownloadImgRespVO();
        downloadImgResp.setFileId(fileId);
        downloadImgResp.setOriginalFilename(fileNameAndContentPair.getLeft().getOriginalFilename());
        downloadImgResp.setContent(Base64.getEncoder().encodeToString(fileNameAndContentPair.getRight()));
        return downloadImgResp;
    }
    
    @Override
    public List<SysFileDownloadImgRespVO> batchDownloadImg(Set<Long> fileIds) {
        if (CollectionUtils.isEmpty(fileIds)) {
            return Collections.emptyList();
        }
        List<SysFilePO> fileInfoList = listByIds(fileIds);
        if (CollectionUtils.isEmpty(fileInfoList)) {
            return Collections.emptyList();
        }
        List<SysFileDownloadImgRespVO> list = new ArrayList<>(16);
        for (SysFilePO po : fileInfoList) {
            FileInfo fileInfo = new FileInfo();
            BeanUtils.copyProperties(po, fileInfo);
            fileInfo.setPlatform(po.getStoragePlatform());
            byte[] body = fileStorageService.download(fileInfo).bytes();
            SysFileDownloadImgRespVO resp = new SysFileDownloadImgRespVO();
            resp.setFileId(po.getId());
            resp.setOriginalFilename(po.getOriginalFilename());
            resp.setContent(Base64.getEncoder().encodeToString(body));
            list.add(resp);
        }
        return list;
    }
    
    /**
     * 文件上传
     *
     * @param file 文件对象
     *
     * @return  文件信息
     */
    private FileInfo doUpload(MultipartFile file) {
        String originalFilename = file.getOriginalFilename();
        return fileStorageService.of(file)
                .setSaveFilename(originalFilename)
                .setPath(LocalDateTime.now().format(PathConstant.FILE_DATE_PATH_FORMATTER) + JdSymbolConstant.SLASH
                        + JdContextUtil.currUserIdNonNull() + JdSymbolConstant.SLASH)
                .upload();
    }

    @Nullable
    @Override
    public SysFileDetailRespVO detail(Long id) {
        Objects.requireNonNull(id, "id cannot be null.");
        SysFilePO po = getById(id);
        if (po == null) {
            return null;
        }
        SysFileDetailRespVO resp = new SysFileDetailRespVO();
        BeanUtils.copyProperties(po, resp);
        
        // 用户所属租户
        String tenant = po.getTenant();
        if (StringUtils.isNotBlank(tenant)) {
            Map<String, String> tenantAndNameMap = sysTenantService.mappingTenantName(Sets.newHashSet(tenant), true);
            resp.setTenantName(tenantAndNameMap.get(tenant));
        }
        return resp;
    }
    
    @Override
    public PageInfo<SysFileListRespVO> list(SysFileListReqVO req) {
        int pageNum = req.getPageNum();
        int pageSize = req.getPageSize();
        String url = req.getUrl();
        String path = req.getPath();
        String basePath = req.getBasePath();
        String storagePlatform = req.getStoragePlatform();
        String originalFilename = req.getOriginalFilename();
        String ext = req.getExt();
        String contentType = req.getContentType();
        String thFilename = req.getThFilename();
        String thContentType = req.getThContentType();
        String tenant = req.getTenant();
    
        // 分页查
        IPage<SysFilePO> pageInfo = new Page<>(pageNum, pageSize);
        IPage<SysFilePO> page = this.baseMapper.selectPage(pageInfo,
                new LambdaQueryWrapper<SysFilePO>()
            .eq(ext != null && ext.trim().length() != 0,  SysFilePO::getExt, ext)
            .likeRight(tenant != null && tenant.trim().length() != 0,  SysFilePO::getTenant, tenant)
            .like(url != null && url.trim().length() != 0,  SysFilePO::getUrl, url)
            .like(path != null && path.trim().length() != 0,  SysFilePO::getPath, path)
            .like(basePath != null && basePath.trim().length() != 0,  SysFilePO::getBasePath, basePath)
            .like(storagePlatform != null && storagePlatform.trim().length() != 0,  SysFilePO::getStoragePlatform, storagePlatform)
            .like(originalFilename != null && originalFilename.trim().length() != 0,  SysFilePO::getOriginalFilename, originalFilename)
            .like(contentType != null && contentType.trim().length() != 0,  SysFilePO::getContentType, contentType)
            .like(thFilename != null && thFilename.trim().length() != 0,  SysFilePO::getThFilename, thFilename)
            .like(thContentType != null && thContentType.trim().length() != 0,  SysFilePO::getThContentType, thContentType)
            .orderByDesc(SysFilePO::getId)
        );
        
        // 查询租户信息
        List<SysFilePO> records = page.getRecords();
        Set<String> tenantSet = records.stream()
                .filter(Objects::nonNull)
                .map(SysFilePO::getTenant).collect(Collectors.toSet());
        Map<String, String> tenantAndNameMap = sysTenantService.mappingTenantName(tenantSet, true);
        
        // 转换为resp模型
        List<SysFileListRespVO> list;
        if (CollectionUtils.isEmpty(records)) {
            list = Collections.emptyList();
        } else {
            list = records.stream().map(po -> {
                SysFileListRespVO resp = new SysFileListRespVO();
                BeanUtils.copyProperties(po, resp);
                resp.setTenantName(tenantAndNameMap.get(resp.getTenant()));
                return resp;
            }).collect(Collectors.toList());
        }
        return PageInfo.of(page.getTotal(), pageNum, pageSize, list);
    }
}
